﻿package com.snzflash.media 
{
	import flash.events.Event;
	import flash.events.EventDispatcher;
	import flash.events.TimerEvent;
	import flash.media.Sound;
	import flash.media.SoundChannel;
	import flash.media.SoundLoaderContext;
	import flash.media.SoundMixer;
	import flash.media.SoundTransform;
	import flash.net.URLRequest;
	import flash.utils.Timer;
	import com.snzflash.events.MediaEvent;
	/**
	 * Аудио плеер, ядро
	 * @author firsoff maxim, tel: +79222345364, skype: maxim.firsoff
	 */
	public class AudioPlayer extends EventDispatcher
	{
		
		// PROPERTIES
	
		// public

		// protected
		protected var _source:String
		protected var _sound:Sound
		protected var _channel:SoundChannel;
		protected var _volume:Number = .60;
		protected var _playHeadPosition:Number = 0;
		protected var _urlRequest:URLRequest;
		protected var _updateTimer:Timer;
		protected var _updateDelay:Number = 200;
		protected var _loadedPercent:Number = 0;
		protected var _playHeadPercent:Number = 0;
		protected var _duration:Number = 0
		// buffer value
		protected var _buffer:Number = 1;
		protected var _state:String;
		// == STATIC==
		
		public static const STATE_PLAYNG:String = 'state_playng';
		public static const STATE_PAUSED:String = 'state_paused';
		public static const STATE_STOPPED:String = 'state_stopped';
		// CONSTRUCTOR
		public function AudioPlayer() 
		{
			init();
		};
		
		/**
		 * initialaize player
		 */
		protected function init():void
		{
			_state = STATE_PLAYNG;
			_updateTimer = new Timer(_updateDelay);
			_updateTimer.addEventListener(TimerEvent.TIMER, onTimer);
		};
		
		
		
		
		// METHODS
	
		// public
		/**
		 * stop playing and preloading audio stream
		 */
		public function stop():void 
		{
			try {_sound.close();}
			catch (err:Error) { };
			destroyChannel();
			_state = STATE_STOPPED;
			_updateTimer.stop();
		};
		
		/**
		 * pause for stream
		 */
		public function pause():void 
		{
			_state = STATE_PAUSED;
			if(!_channel) return
			_playHeadPosition = _channel.position;
			_channel.stop();
		};
		
		/**
		 * playing stream
		 */
		public function play():void 
		{
			//trace(_state, _playHeadPosition);
			_updateTimer.start();
			//trace(_state)
			switch (_state) 
			{
				case STATE_PAUSED:
					if (_sound) _channel = _sound.play(_playHeadPosition);
				break;
				
				case STATE_PLAYNG:
					if (_sound)_channel = _sound.play(_playHeadPosition);
					return;
				break;
				
				case STATE_STOPPED:
					if (_sound)_channel = _sound.play(_playHeadPosition);
				break;
			};
			_state = STATE_PLAYNG;
			if(_channel) _channel.addEventListener(Event.SOUND_COMPLETE, onSoundComplete);
			volume = _volume;
		};
		
		public function seek(position:Number = 0):void 
		{
			_playHeadPosition = position;
			seekPosition(_playHeadPosition);
		};
		
		public function seekByPercent(percent:Number = 0):void 
		{
			_playHeadPosition = percent * _duration;
			seekPosition(_playHeadPosition);
		}
		
		
		public function destroy():void 
		{
			destroySoundInstance();
			_updateTimer.stop();
			_updateTimer.removeEventListener(TimerEvent.TIMER, onTimer);
			_updateTimer = null;
			_sound = null;
			_channel = null;
		}
		// protected
		
		protected function seekPosition(position:Number = 0):void 
		{
			if (_state == STATE_PLAYNG) 
			{
				// уничтожаем текущий канал?
				destroyChannel();
				_channel = _sound.play(position);
				_channel.addEventListener(Event.SOUND_COMPLETE, onSoundComplete);
				volume = _volume;
			}
		}
		
		protected function destroySoundInstance():void
		{
			try {_sound.close();}
			catch (err:Error) { };
			destroyChannel();
		};
		
		
		protected function destroyChannel():void 
		{
			try 
			{
				_channel.stop();
				_channel.removeEventListener(Event.SOUND_COMPLETE, onSoundComplete);
			}
			catch (err:Error) { };
		}
		// getters/setters
		/**
		 * URL track
		 */
		public function get source():String { return _source; };
		
		public function set source(value:String):void 
		{
			
			
			_source = value;
			_urlRequest = new URLRequest(value);
			destroySoundInstance();
			var b:SoundLoaderContext = new SoundLoaderContext(_buffer);
			_sound = new Sound(_urlRequest, b);
			_playHeadPosition = _playHeadPercent = _loadedPercent = 0;
			
			// если авто загрузка то проигрываем
			//trace(this, _state);
			if (_state == STATE_PLAYNG) 
			{
				
				_channel = _sound.play();
				_channel.addEventListener(Event.SOUND_COMPLETE, onSoundComplete);
			}
			
			_updateTimer.start();
			
			volume = _volume;
			
		};
		
		/**
		 * Sound instance
		 */
		public function get sound():Sound { return _sound; };
		
		public function get channel():SoundChannel { return _channel; };
		
		public function get volume():Number { return _volume; };
		
		public function set volume(value:Number):void 
		{
			_volume = value;
			var st:SoundTransform = new SoundTransform(value);
			if (_channel) _channel.soundTransform = st;
		};
		
		public function get playHeadPercent():Number { return _playHeadPercent; };
		
		
		public function get state():String { return _state; };
		
		public function get updateDelay():Number { return _updateDelay; };
		
		public function set updateDelay(value:Number):void 
		{
			_updateDelay = value;
			_updateTimer.delay = value;
		};
		
		public function get loadedPercent():Number { return _loadedPercent; };
		
		public function set state(value:String):void 
		{
			_state = value;
		};
		
		public function get buffer():Number { return _buffer; };
		
		public function set buffer(value:Number):void 
		{
			_buffer = value;
		};
		
		// == STATIC==
		
		
		// HANDLERS
		
		protected function onTimer(event:TimerEvent = null):void 
		{
			if (!_sound || !_channel) return;
			_loadedPercent = _sound.bytesLoaded / _sound.bytesTotal;
			// общая длина звука
			var total:Number = _sound.bytesTotal;
			var position:Number = _channel.position;
			var length:Number = _sound.length;
			
			if (total > 0) 
			{
				length /= _loadedPercent;
				_duration = length;
				// если мы играем, то расчет такой позиции
				if (_state == STATE_PLAYNG) 
				{ 
					_playHeadPercent = position / length; 
				}
				else 
				{
					_playHeadPercent = _playHeadPosition / length; 
				}
			};
			
			//trace('play : ' + _playHeadPercent, 'laded: ' + _loadedPercent);
			var e:MediaEvent = new MediaEvent(MediaEvent.PLAYING_PROGRESS, false, false, _playHeadPercent);
			dispatchEvent(e);
			
			var e2:MediaEvent = new MediaEvent(MediaEvent.LOADING_PROGRESS, false, false, _loadedPercent);
			dispatchEvent(e2);
		};
		
		protected function onSoundComplete(event:Event):void 
		{
			//trace('COMPLETE');
			_updateTimer.stop();
			onTimer(null);
			dispatchEvent(event.clone());
		};
	};

};